"""
    GIL 全局解释器锁 --- 多线程在**python解释器**的作用下，抢锁的行为
    GIL使Python中的多线程的在执行中，同一时刻，只有一个线程在执行。
    多线程要比多进程更节约资源，这仅仅是解释器的问题。
    GIL只对线程有影响！！！对进程/协程，不受影响。
    GIL跟Python语言本身无关，跟解释器有关，默认解释器 CPython，CPython有GIL问题。
    GIL因为历史原因，无法从CPython解释器中去除，所以在Python中，要想发挥多核CPU的能力，请使用多进程。

    问题：多线程抓取的程序 是否 比单线程抓取 性能有提升？
    答案：是的， 虽然有GIL锁，但是当线程切换过程中，当出现阻塞的时候，会切换另一个线程执行，这样可以省掉阻塞等待的时间。

    1- windows的QQ.exe不能安装到linux系统上，C语言把代码通过编译器变成二进制给linux识别
    2- Python解释器，也成为虚拟机，就是自己安装的python 3.7 interpreter解释器的意思，python文件运行在这个虚拟的运行环境
    3- C语言通过不同的编译器运行在不同的系统下，是一种编译性语言，说完5分钟，才开始翻译，浪费了大量的编译时间，省去运行时间
    4- Python通过一个python解释器虚拟环境运行在不同操作系统下，是一种解释性语言，类似于同声传译，翻译一行，运行一行，编译时间短，运行时间长

    5- python多线程情况下是一种**假的多线程**，多个线程抢不同的运行时间，这种现象就叫做GIL，全局解释器锁
    6- 要想解决GIL全局解释器锁，要么**换python解释器**，要么**python调用c语言启动多线程**
        胶水语言：python可以调用 java/c/c++/js
"""

"""
    1- python语言和GIL没有半毛钱关系，仅仅是由于历史原因在Cpython虚拟机(解释器)，难以移除GIL
    2- GIL：每个线程在执行的时候都需要获取GIL锁，保证同一时刻只有一个线程可以执行代码
    3- 线程释放GIL锁的情况：在IO操作可能引起阻塞之前，暂时释放gil，但是执行完后必须重新获取GIL
    4- Python使用**多进程**是可以调用多核cpu资源的

    为什么python中有了GIL保证统一时刻只有一个线程执行代码为什么线程还需要互斥锁？
    1- GIL只能保证同一时刻只有一个线程在做，但是不能保证切换的过程中这个事情被该线程完成了
    2- 互斥锁能够保证在线程切换之前，事情做完，取值 -- 修改 -- 赋值
    3- 类似于厨房规定一个线程只能用10分钟做饭，如果每个线程都能在10分钟内做完走出厨房，那么GIL就没问题
    4- 但是如果有的线程没有在10分钟内完成，切换到另一个线程去厨房做饭，那么只能在前线程的基础上进行修改
    
    使用场景：
        计算密集型(大量时间做计算，做事情过程中没有阻塞/等待) --> 多进程
        IO密集型(中间没有阻塞/等待，如 文件的读写，网络的收与发) --> 多线程，协程
"""

"""
    1- libdead_loop.so是编译好的二进制C语言
    2- 主线程需要使用python解释器运行，但是执行编译好的二进制c语言文件不再需要python解释器
    3- 这样可以实现两个线程同时运行，占用两个核心，而不再是假的多线程
    
"""
from ctypes import *
from threading import Thread

"""
#include "pch.h"

double Addition(double a, double b)
{
    return a + b;
}

double Subtraction(double a, double b)
{
    return a - b;
}

double Multiplication(double a, double b)
{
    return a * b;
}

double Division(double a, double b)
{
    return a / b;
}

"""


# 加载动态库
lib = cdll.LoadLibrary("./SimpleMath_x64.dll")
# 执行方法名
func = lib.Multiplication
# 指定传入参数类型
func.argtypes = [c_double, c_double]
# 指定传入参数类型
func.restype = c_double
# 调用的动态库
res = func(c_double(30), c_double(50))

print(type(res))
print(str(res))

print('=' * 100)


# 获取线程的返回值
class MyThread(Thread):

	def __init__(self, func, args=()):
		super(MyThread, self).__init__()
		self.func = func
		self.args = args

	def run(self):
		self.result = self.func(*self.args)

	def get_result(self):
		try:
			return self.result  # 如果子线程不使用join方法，此处可能会报没有self.result的错误
		except Exception:
			return None


# 创建一个子线程，让其执行c语言编写的函数
thd = MyThread(func, args=[c_double(120), c_double(200)])
thd.start()

thd.join()
res = thd.result
print(res)

# 主线程
# while True:
# 	pass

"""
线程、进程、协程的区别:

进程拥有自己独立的堆和栈，既不共享堆，亦不共享栈，进程由操作系统调度。

线程拥有自己独立的栈和共享的堆，共享堆，不共享栈，线程亦由操作系统调度(标准线程是的)。

协程和线程一样共享堆，不共享栈，协程由程序员在协程的代码里显示调度。

一个应用程序一般对应一个进程，一个进程一般有一个主线程，还有若干个辅助线程，线程之间是平行运行的，在线程里面可以开启协程，让程序在特定的时间内运行。

协程和线程的区别是：协程避免了无意义的调度，由此可以提高性能，但也因此，程序员必须自己承担调度的责任，同时，协程也失去了标准线程使用多CPU的能力。

------------------------------------------------------------------------------------------------------------------

协同程序（coroutine）与多线程情况下的线程比较类似：有自己的堆栈，自己的局部变量，有自己的指令指针（IP，instruction pointer），但与其它协同程序共享全局变量等很多信息。

协程(协同程序): 同一时间只能执行某个协程。开辟多个协程开销不大。协程适合对某任务进行分时处理。

线程: 同一时间可以同时执行多个线程。开辟多条线程开销很大。线程适合多任务同时处理。

.协程，即协作式程序，其思想是，一系列互相依赖的协程间依次使用CPU，每次只有一个协程工作，而其他协程处于休眠状态。协程实际上是在一个线程中，只不过每个协程对CUP进行分时，协程可以访问和使用unity的所有方法和component

.线程，多线程是阻塞式的，每个IO都必须开启一个新的线程，但是对于多CPU的系统应该使用thread，尤其是有大量数据运算的时刻，但是IO密集型就不适合；而且thread中不能操作unity的很多方法和component
"""
